Add GeocachingDB input and output module.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 22 Jan 2003 03:35:29 +0000 (03:35 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 22 Jan 2003 03:35:29 +0000 (03:35 +0000)
gpsbabel/Makefile
gpsbabel/README
gpsbabel/gcdb.c [new file with mode: 0644]
gpsbabel/reference/GeocachingDB.PDB [new file with mode: 0644]
gpsbabel/testo
gpsbabel/vecs.c

index ff7fb391a42f2f7956738cef2fc42fe919bb1295..62e75019888c5892042d0b9559cfe104df822627 100644 (file)
@@ -3,7 +3,7 @@ CFLAGS=-g -Icoldsync
 FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \
        gpsutil.o tiger.o pcx.o csv.o cetus.o gpspilot.o magnav.o \
        psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \
-       xcsv.o xmapwpt.o
+       xcsv.o xmapwpt.o gcdb.o
 
 JEEPS=jeeps/gpsapp.o jeeps/gpscom.o jeeps/gpsfmt.o jeeps/gpsinput.o \
        jeeps/gpsmath.o jeeps/gpsmem.o  \
index 0979321b37c33b1962af89b1fac27679d57e1621..578eaaa94f9a75798be96b80ab32d7e9ce6d39f0 100644 (file)
@@ -279,6 +279,18 @@ THE FORMATS
         against GpsDrive v 1.30 found @ http://www.kraftvoll.at/software.
         Contributed by Alan Curry.
 
+    Geocaching DB
+
+       This is a PDA file format.    It was tested against version 2
+       of GeocachingDB and a development snapshot of version 3.  
+       Information on the file format came from Dougs Brat and Ron Parker.
+       A particularly handy way to use GPSBabel on these files is to use
+       GPSBabel to read a GPX file with Groundspeak  (geocaching.com) 
+       extensions and let it write you a GeocachingDB file that contains
+       the cache names, difficulty, terrain, and such.
+
+       http://vip.hyperusa.com/~dougs/geocachingdb/geocachingdb.htm
+
 
 COMMON USAGE
 
diff --git a/gpsbabel/gcdb.c b/gpsbabel/gcdb.c
new file mode 100644 (file)
index 0000000..2021b66
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+    Read and write GeocachingPDB files.
+
+    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include "coldsync/palm.h"
+#include "coldsync/pdb.h"
+
+#define MYNAME "GeocachingDB"
+#define MYTYPE  0x44415441     /* DATA */
+#define MYCREATOR 0x42726174   /* Brat */
+
+#define MAXRECSZ 500 /* This is overkill as the records seem to be around 100
+                        bytes a piece, but being conservative and dealing 
+                        with realloc issues just doesn't seem worth it. */
+
+typedef enum {
+       RECTYPE_TEXT = 0,
+       RECTYPE_DATE = 2
+} gcdb_rectype;
+
+struct dbfld {
+       char    fldname[4];
+       pdb_16  fldtype;
+       pdb_16  fldlen;
+};
+
+struct dbrec {
+       pdb_16  nflds;
+       struct dbfld dbfld[1];
+};
+
+static FILE *file_in;
+static FILE *file_out;
+static const char *out_fname;
+struct pdb *opdb;
+struct pdb_record *opdb_rec;
+
+
+static void
+rd_init(const char *fname, const char *args)
+{
+       file_in = fopen(fname, "rb");
+       if (file_in == NULL) {
+               fatal(MYNAME ": Cannot open %s for reading\n", fname);
+       }
+}
+
+static void
+rd_deinit(void)
+{
+       fclose(file_in);
+}
+
+static void
+wr_init(const char *fname, const char *args)
+{
+       file_out = fopen(fname, "wb");
+       out_fname = fname;
+       if (file_out == NULL) {
+               fatal(MYNAME ": Cannot open %s for writing\n", fname);
+       }
+}
+
+static void
+wr_deinit(void)
+{
+       fclose(file_out);
+}
+
+static void
+data_read(void)
+{
+       struct dbrec *rec;
+       struct pdb *pdb;
+       struct pdb_record *pdb_rec;
+
+       if (NULL == (pdb = pdb_Read(fileno(file_in)))) {
+               fatal(MYNAME ": pdb_Read failed\n");
+       }
+
+       if ((pdb->creator != MYCREATOR) || (pdb->type != MYTYPE)) {
+               fatal(MYNAME ": Not a GeocachingDB file.\n");
+       }
+
+       for(pdb_rec = pdb->rec_index.rec; pdb_rec; pdb_rec=pdb_rec->next) {
+               waypoint *wpt = xcalloc(sizeof(*wpt),1);
+               struct dbrec *rec = (struct dbrec *) pdb_rec->data;
+               int nflds;
+               int length;
+               int type;
+               int i;
+               char *recdata;
+               int lat_dir = 0;
+               int lat_deg = 0;
+               float lat_min = 0.0;
+               int lon_dir = 0;
+               int lon_deg = 0;
+               float lon_min = 0.0;
+
+               nflds = be_read16(&rec->nflds);
+               recdata = (char *) &rec->dbfld[nflds];
+
+               for (i = 0; i < nflds; i++) {
+                       length = (unsigned short) be_read16(&rec->dbfld[i].fldlen);
+                       type = be_read16(&rec->dbfld[i].fldtype);
+
+                       switch(type) {
+                       case RECTYPE_TEXT: /* Text */
+                               if (!strncmp("gcid", rec->dbfld[i].fldname,4)) {
+                                       wpt->shortname = xstrdup(recdata);
+                               } else 
+                               if (!strncmp("gcna", rec->dbfld[i].fldname,4)) {
+                                       wpt->description = xstrdup(recdata);
+                               } else 
+                               if (!strncmp("lat0", rec->dbfld[i].fldname,4)) {
+                                       lat_dir = *recdata == 'N' ? 1 : -1;
+                               } else 
+                               if (!strncmp("lat1", rec->dbfld[i].fldname,4)) {
+                                       lat_deg = atoi(recdata);
+                               } else 
+                               if (!strncmp("lat2", rec->dbfld[i].fldname,4)) {
+                                       lat_min = atof(recdata);
+                               } 
+                               if (!strncmp("lon0", rec->dbfld[i].fldname,4)) {
+                                       lon_dir = *recdata == 'E' ? 1 : -1;
+                               } else 
+                               if (!strncmp("lon1", rec->dbfld[i].fldname,4)) {
+                                       lon_deg = atoi(recdata);
+                               } else 
+                               if (!strncmp("lon2", rec->dbfld[i].fldname,4)) {
+                                       lon_min = atof(recdata);
+                               } else
+                               if (!strncmp("take", rec->dbfld[i].fldname,4)) {
+                                       wpt->notes = xstrappend(wpt->notes,  " Took ");
+                                       wpt->notes = xstrappend(wpt->notes,  recdata);
+                               } else
+                               if (!strncmp("left", rec->dbfld[i].fldname,4)) {
+                                       wpt->notes = xstrappend(wpt->notes, " Left ");
+                                       wpt->notes = xstrappend(wpt->notes,  recdata);
+                               } else
+                               if (!strncmp("diff", rec->dbfld[i].fldname,4)) {
+                                       wpt->gc_data.diff = 10 * atof(recdata);
+                               } else
+                               if (!strncmp("terr", rec->dbfld[i].fldname,4)) {
+                                       wpt->gc_data.terr = 10 * atof(recdata);
+                               } 
+                               break;
+#if 0
+                               /* This really is the date of the find, 
+                                * not the cache creation date.   
+                                */
+                       case RECTYPE_DATE:
+                               if (!strncmp("date", rec->dbfld[i].fldname,4)) {
+                                       time_t tm;
+                                       tm = be_read32(recdata) * 24 * 3600;
+                                       tm -= EPOCH_1904;
+                                       wpt->creation_time = tm;
+                                       fprintf(stderr, "date %d\n", tm);
+                               }
+                               break;
+#endif
+                       }
+                       recdata += (length + 1) & (~1);
+               }
+               wpt->position.latitude.degrees = lat_dir * (lat_deg + lat_min/60);
+               wpt->position.longitude.degrees = lon_dir * (lon_deg + lon_min/60);
+               waypt_add(wpt);
+       }
+
+       free_pdb(pdb);
+}
+
+static int 
+gcdb_add_to_rec(struct dbrec *rec, char *fldname, gcdb_rectype rectype, void *data)
+{
+       int length;
+       static char *tbuf;
+       static char *tbufp;
+       static int rec_cnt;
+
+       if (fldname == NULL) {
+               length = tbufp - tbuf;
+               be_write16(&rec->nflds, rec_cnt);
+               memcpy(&rec->dbfld[rec_cnt],tbuf, length);
+               tbufp = tbuf;
+               length += 4 + sizeof(struct dbfld) * rec_cnt;
+               return length;
+       }
+
+       if (!tbuf) {
+               tbuf = xcalloc(MAXRECSZ, 1);
+               tbufp = tbuf;
+       }
+
+       be_write16(&rec->dbfld[rec_cnt].fldtype,rectype); 
+       strncpy(rec->dbfld[rec_cnt].fldname, fldname, 4);
+
+       switch (rectype) {
+       case RECTYPE_TEXT:
+               length = 1 + strlen(data);
+               be_write16(&rec->dbfld[rec_cnt].fldlen, length);
+               strcpy(tbufp, data);
+               tbufp += (length + 1) & (~1);
+               break;
+       case RECTYPE_DATE:
+               length = 4;
+               be_write16(&rec->dbfld[rec_cnt].fldlen, length);
+               be_write32(tbufp, ((time_t)data - EPOCH_1904)/ (3600 * 24));
+               tbufp += length;
+               break;
+       default:
+               abort();
+       }
+       rec_cnt++;
+}
+
+static void
+gcdb_write_wpt(const waypoint *wpt)
+{
+       struct dbrec *rec;
+       char *vdata;
+       char *recdata;
+       static int ct;
+       int length;
+       int reclen;
+       char tbuf[100];
+
+       /*
+        * We don't really know how many fields we'll have or how long
+        * they'll be so we'll just lazily create a huge place to hold them.
+        */
+       rec = xcalloc(sizeof(*rec) + 5000, 1);
+
+       gcdb_add_to_rec(rec, "gcna", RECTYPE_TEXT, wpt->description);
+       gcdb_add_to_rec(rec, "gcid", RECTYPE_TEXT, wpt->shortname);
+
+       gcdb_add_to_rec(rec, "lat0", RECTYPE_TEXT, 
+                       wpt->position.latitude.degrees < 0 ? "S" : "N");
+
+       sprintf(tbuf, "%d", (int) wpt->position.latitude.degrees);
+       gcdb_add_to_rec(rec, "lat1", RECTYPE_TEXT, tbuf);
+
+       sprintf(tbuf, "%f", 60 * (wpt->position.latitude.degrees - 
+                               (int) wpt->position.latitude.degrees));
+       gcdb_add_to_rec(rec, "lat2", RECTYPE_TEXT, tbuf);
+
+
+       gcdb_add_to_rec(rec, "lon0", RECTYPE_TEXT, 
+                       wpt->position.longitude.degrees < 0 ? "W" : "E");
+
+       sprintf(tbuf, "%d", (int) wpt->position.longitude.degrees);
+       gcdb_add_to_rec(rec, "lon1", RECTYPE_TEXT, tbuf);
+
+       sprintf(tbuf, "%f", 60 * (wpt->position.longitude.degrees - 
+                               (int) wpt->position.longitude.degrees));
+       gcdb_add_to_rec(rec, "lon2", RECTYPE_TEXT, tbuf);
+
+       if (wpt->gc_data.diff) {
+               sprintf(tbuf, "%f", wpt->gc_data.diff / 10.0);
+               gcdb_add_to_rec(rec, "diff", RECTYPE_TEXT, tbuf);
+       }
+
+       if (wpt->gc_data.terr) {
+               sprintf(tbuf, "%f", wpt->gc_data.terr / 10.0);
+               gcdb_add_to_rec(rec, "terr", RECTYPE_TEXT, tbuf);
+       }
+
+#if 0
+                               /* This really is the date of the find, 
+                                * not the cache creation date.   
+                                */
+       if (wpt->creation_time) {
+               gcdb_add_to_rec(rec, "date", RECTYPE_DATE, (void *) wpt->creation_time);
+       }
+#endif
+
+       /*
+        * We're done.  Build the record.
+        */
+       reclen = gcdb_add_to_rec(rec, NULL, 0, NULL);
+
+       opdb_rec = new_Record(0, 2, ct++, reclen, (const ubyte *)rec);
+
+       if (opdb_rec == NULL) {
+               fatal(MYNAME ": libpdb couldn't create record\n");
+       }
+
+       if (pdb_AppendRecord(opdb, opdb_rec)) {
+               fatal(MYNAME ": libpdb couldn't append record\n");
+       }
+
+       free(rec);
+}
+
+static void
+data_write(void)
+{
+       
+       if (NULL == (opdb = new_pdb())) { 
+               fatal (MYNAME ": new_pdb failed\n");
+       }
+
+       strncpy(opdb->name, out_fname, PDB_DBNAMELEN);
+       strncpy(opdb->name, "GeocachingDB", PDB_DBNAMELEN);
+       opdb->name[PDB_DBNAMELEN-1] = 0;
+       opdb->attributes = PDB_ATTR_BACKUP;
+       opdb->ctime = opdb->mtime = time(NULL) + 2082844800U;
+       opdb->type = MYTYPE;  /* CWpt */
+       opdb->creator = MYCREATOR; /* cGPS */
+       opdb->version = 1;
+
+       waypt_disp_all(gcdb_write_wpt);
+
+       pdb_Write(opdb, fileno(file_out));
+}
+
+
+ff_vecs_t gcdb_vecs = {
+       rd_init,
+       wr_init,
+       rd_deinit,
+       wr_deinit,
+       data_read,
+       data_write,
+};
diff --git a/gpsbabel/reference/GeocachingDB.PDB b/gpsbabel/reference/GeocachingDB.PDB
new file mode 100644 (file)
index 0000000..4f6c38b
Binary files /dev/null and b/gpsbabel/reference/GeocachingDB.PDB differ
index 1955661be307babb4ae0cbb8ab9bbd29a035d995..0ca356992ccc0965301c4ad0639aef3b7be8b579 100755 (executable)
@@ -195,3 +195,15 @@ ${PNAME} -i mapsource -f reference/mapsource.mps  -o gpx -F ${TMPDIR}/ms1.gpx
 ${PNAME} -i mapsource -f reference/mapsource.mps  -o mapsource -F ${TMPDIR}/ms.mps
 ${PNAME} -i mapsource -f ${TMPDIR}/ms.mps -o gpx -F ${TMPDIR}/ms2.gpx
 diff ${TMPDIR}/ms1.gpx ${TMPDIR}/ms2.gpx
+
+#
+# Geocaching Database is a binary Palm format that, like the GPX variants
+# has a zillion "equivalent" encodings of any given record set.  So we
+# read the reference file, spin it to GPX and back to GCDB and then spin
+# that one to GPX.
+#
+
+${PNAME} -i gcdb -f reference/GeocachingDB.PDB -o gpx -F ${TMPDIR}/gcdb1.gpx \
+               -o gcdb -F ${TMPDIR}/gcdb1.pdb
+${PNAME} -i gpx -f ${TMPDIR}/gcdb1.gpx -o gpx -F ${TMPDIR}/gcdb2.gpx
+diff ${TMPDIR}/gcdb1.gpx ${TMPDIR}/gcdb1.gpx
index b6e7ddc87f8e88e9b5a82ce71fa7f9ffb52466a9..f273878f5b5807ba80b155d59336fd5ea55f66dc 100644 (file)
@@ -54,6 +54,7 @@ extern ff_vecs_t xmap_vecs;
 extern ff_vecs_t xmapwpt_vecs;
 extern ff_vecs_t tmpro_vecs;
 extern ff_vecs_t gpsdrive_vecs;
+extern ff_vecs_t gcdb_vecs;
 
 static
 vecs_t vec_list[] = {
@@ -206,6 +207,12 @@ vecs_t vec_list[] = {
                "GpsDrive Format", 
                NULL
        },
+       {
+               &gcdb_vecs,
+               "gcdb",
+               "Geocaching Database", 
+               NULL
+       },
 
         {
                NULL,